/**
 * Looks like simple ImageButton,
 * but opens file upload dialog when clicking on it.
 *
 * Note: when upload method calls, file upload sends request to server.
 * The server must retrieve JSON response to handle it properly.
 * When the server returned JSON, onFileUploaded called with info parameter
 * that contains response object.
 *
 * @author Anrey Anisimov <hercules@inbox.ru>
 */

import core.url.Encoder;
import core.ui.Event;
import core.ui.ImageButton;
import core.util.Listeners;

class FileUpload extends ImageButton {

	/**
	 * Initializes the file upload control.
	 *
	 * @param parent (Window) Parent window
	 * @param x, y (int) x and y coordinates
	 * @param width, height (int) Size of control
	 */
	this.FileUpload = function(parent, x, y, width, height) {
		super(parent, x, y, width, height);
		this.path = '';
		this.url = '';
		this.listeners = new Listeners([
            'onFileUploaded',
            'onUploadError',
            'onFileSelected'
        ]);
	}

	this.addUploadListener = function(listener) {
		this.listeners.add(listener);
	}

	this.removeUploadListener = function(listener) {
		this.listeners.remove(listener);
	}

	/**
	 * Uploads selected file to the server.
	 *
	 * @param params (object) [optional] request params.
	 */
	this.upload = function(params) {
		// Place params in the query string
		var arr = [];
		for (var name in params) {
			arr.push(Encoder.encode(name) + '=' + Encoder.encode(params[name]));
		}

		this.form.action = this.url;
		if (arr.length > 0) this.form.action += ('?' + arr.join('&'));
		this.form.submit();
	}

	/**
	 * Cleans up control.
	 */
	this.clear = function() {
		this.form.reset();
	}

	/**
	 * Checks whether the file is selected.
	 *
	 * @return bool
	 */
	this.isFileSelected = function() {
		return (this.path.length > 0);
	}

	/**
	 * Returns selected file path.
	 *
	 * @return string
	 */
	this.getFilePath = function() {
		return this.path;
	}
	
	/**
	 * Sets the file upload url.
	 *
	 * @param url (string) new url
	 */
	this.setUploadUrl = function(url) {
		this.url = url;
	}
	
	/**
	 * Returns currently used upload url.
	 *
	 * @return string.
	 */
	this.getUploadUrl = function() {
		return this.url;
	}
	
	/**
	 * Sets the file upload's button text.
	 *
	 * @param text (string) new button text
	 */
	this.setText = function(text) {
		this.text.innerHTML = text;
	}
	
	/**
	 * Returns current file upload text.
	 *
	 * @return string
	 */
	this.getText = function() {
		return this.text.innerHTML;
	}

	this.close = function() {
		this.getElement().removeChild(this.iframe);
		this.form.removeChild(this.input);
		this.getElement().removeChild(this.form);
		delete this.iframe;
		delete this.input;
		delete this.form;
		super.close();
	}
	
	this.setFocus = function() {
		// HACK: Do nothing since file input doesn't work in IE when loosing focus.
	}
	
	/**
	 * @protected
	 * Creates root DOM element for file upload control.
	 *
	 * @return HTMLElement
	 */
	this.createElement = function() {
		var root = super.createElement();
		
		this.text = document.createElement('div');
		this.text.style.position = 'absolute';
		this.text.style.left = this.text.style.top = '0px';
		this.text.style.width = this.text.style.height ='100%';
		this.text.style.zIndex = 0;
		root.appendChild(this.text);
		
		// Create hidden iframe
		this.iframe = document.createElement('iframe');
		this.iframe.id = this.iframe.name = (this.getId() + '_iframe');
		this.iframe.style.display = 'none';
		this.iframe.style.width = this.iframe.style.height = '0px';
		this.iframe.style.margin = this.iframe.style.padding = '0px';
		root.appendChild(this.iframe);
		
		Event.addListener(this.iframe, 'load', bind(this, this.onIframeLoad));
		
		// Create form
		this.form = document.createElement('form');
		this.form.target = this.iframe.name;
		this.form.method = 'POST';
		this.form.enctype = this.form.encoding = 'multipart/form-data';
		this.form.style.margin = this.form.style.padding = '0px';
		root.appendChild(this.form);
		
		// Create opaque input[type=file]
		this.input = document.createElement('input');
		this.input.name = this.input.type = 'file';
		this.input.style.position = 'absolute';
		this.input.style.zIndex = 1;
		this.input.style.opacity = 0;
		this.input.style.MozOpacity = 0;		// Mozilla case
		this.input.style.KhtmlOpacity = 0;	// Konqueror
		this.input.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)';
		this.input.style.styleFloat = this.input.style.cssFloat = 'right';
		this.input.style.height = '100%';
		this.input.style.right = '0px';
		this.input.onchange = bind(this, this.onFileInputChange);
		this.form.appendChild(this.input);
		
		return root;
	}

	/**
	 * @protected
	 * @param response
	 */
	this.fireFileUploaded = function(response) {
		this.listeners.call('onFileUploaded', [this, response]);
	}

	/**
	 * @protected
	 * @param response
	 */
	this.fireUploadError = function(response) {
		this.listeners.call('onUploadError', [this, response]);
	}

	/**
	 * @protected
	 */
	this.fireFileSelected = function() {
		this.listeners.call('onFileSelected', [this]);
	}

	this.onIframeLoad = function() {
		
		// HACK: IE opens new window with response text.
		if (!this.iframe.contentWindow.name)
			this.iframe.contentWindow.name = this.iframe.name;
		
		// Parse the response
		var respText = this.iframe.contentWindow.document.body.innerHTML;
		if (respText.length > 0) {
			
			var status = 'FAILED';
			var message = '';
			
			try {
				// The server should retrieve response in JSON format
				var response = eval('(' + respText + ')');
				status = response.status;
				message = response.message;
			} catch (ex) {
				message = respText;
			}
			
			if ('OK' == status) {
				this.fireFileUploaded(response);
			} else {
				this.fireUploadError(response);
			}
		}
	}

	this.onFileInputChange = function() {
		this.path = this.input.value;
		this.fireFileSelected();
		this.form.reset();
	}
	
	this.onmousemove = function(event) {
		// Make file input always under mouse cursor.
		// 15 - for Konqueror's small file icon
		this.input.style.right = this.getAbsX() + this.getWidth() - event.getX() - 15 + 'px';
		this.input.style.top = event.getY() - this.getAbsY() - this.input.offsetHeight/2 + 'px';
	}
}
